
#include "VkHelper.h"

const DriverFeature validationInstanceLayers[] = {
    {"VK_LAYER_KHRONOS_validation", false, true}};

const DriverFeature validationInstanceExtensions[] = {
    {"VK_KHR_surface", false, true},
    {"VK_KHR_win32_surface", false, true}};

const DriverFeature validationDeviceExtensions[] = {
    {VK_KHR_SWAPCHAIN_EXTENSION_NAME, false, true}};

VkBool32 debugReportCallback(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
                             size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg, void *pUserData)
{
        if (msgFlags == VK_DEBUG_REPORT_ERROR_BIT_EXT)
        {
                Error("%s: [%s] Code %d : %s", "Error", pLayerPrefix, msgCode, pMsg);
        }
        else if (msgFlags == VK_DEBUG_REPORT_WARNING_BIT_EXT)
        {
                Warn("%s: [%s] Code %d : %s", "Warning", pLayerPrefix, msgCode, pMsg);
        }
        else
        {
                Print("%s: [%s] Code %d : %s", "Info", pLayerPrefix, msgCode, pMsg);
        }
        return VK_FALSE;
}

void VkHelper::createInstance(bool bValidate, VkInstance *out_instance, VkDebugReportCallbackEXT *out_debugReport)
{
        // instance layers
        uint32_t availableLayerCount;
        CALL_VK(vkEnumerateInstanceLayerProperties(&availableLayerCount, nullptr));
        VkLayerProperties availableLayerProps[availableLayerCount];
        CALL_VK(vkEnumerateInstanceLayerProperties(&availableLayerCount, availableLayerProps));
        Print("---------------------------------");
        const char *enabledLayerNames[32] = {};
        uint32_t enabledLayerCount = 0;
        checkFeatures("Instance Layers", bValidate, false, validationInstanceLayers, ARRAY_SIZE(validationInstanceLayers),
                      availableLayerProps, availableLayerCount, enabledLayerNames, &enabledLayerCount);

        // instance extensions
        uint32_t availableExtensionCount;
        CALL_VK(vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, nullptr));
        VkExtensionProperties availableExtensionProps[availableExtensionCount];
        CALL_VK(vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, availableExtensionProps));
        Print("---------------------------------");
        const char *enableExtensionNames[32] = {};
        uint32_t enableExtensionCount = 0;
        checkFeatures("Instance Extensions", bValidate, true, validationInstanceExtensions, ARRAY_SIZE(validationInstanceExtensions),
                      availableExtensionProps, availableExtensionCount, enableExtensionNames, &enableExtensionCount);

        VkApplicationInfo application = {
            .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
            .pNext = nullptr,
            .pApplicationName = APP_NAME,
            .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
            .pEngineName = APP_NAME,
            .engineVersion = VK_MAKE_VERSION(1, 0, 0),
            .apiVersion = VK_VERSION_1_0};

        VkInstanceCreateInfo createInfo = {
            .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .pApplicationInfo = &application,
            .enabledLayerCount = enabledLayerCount,
            .ppEnabledLayerNames = enabledLayerCount == 0 ? nullptr : enabledLayerNames,
            .enabledExtensionCount = enableExtensionCount,
            .ppEnabledExtensionNames = enableExtensionCount == 0 ? nullptr : enableExtensionNames};

        // 在创建VkInstance之前
        if (bValidate)
        {
                VkDebugReportCallbackCreateInfoEXT debugReport = {
                    .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
                    .pNext = nullptr,
                    .flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |              // 错误信息
                             VK_DEBUG_REPORT_WARNING_BIT_EXT |            // 警告信息
                             VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, // 性能警告信息
                    .pfnCallback = (PFN_vkDebugReportCallbackEXT)debugReportCallback,
                    .pUserData = nullptr,
                };

                createInfo.pNext = &debugReport;
        }

        CALL_VK(vkCreateInstance(&createInfo, VK_ALLOC, out_instance));
}

void VkHelper::pickPhyDevAndCreateDev(VkInstance instance, VkSurfaceKHR surface, DeviceInfo *out_deviceInfo, QueueInfo *out_queueInfo)
{
        uint32_t physicalDevCount;
        CALL_VK(vkEnumeratePhysicalDevices(instance, &physicalDevCount, nullptr));
        VkPhysicalDevice physicalDevs[physicalDevCount];
        CALL_VK(vkEnumeratePhysicalDevices(instance, &physicalDevCount, physicalDevs));

        Print("---------------------------------");
        Print("Physical Devices:");
        // 选取的物理设备和队列索引
        VkPhysicalDevice selectedPhysicalDev = nullptr;
        uint16_t workQueueIndex;
        uint16_t presentQueueIndex;

        for (int i = 0; i < physicalDevCount; i++)
        {
                VkPhysicalDeviceProperties devProps;
                vkGetPhysicalDeviceProperties(physicalDevs[i], &devProps);

                // 打印Log，不影响流程
                printPhysicalDevLog(devProps);

                uint32_t queueFamilyPropCount;
                vkGetPhysicalDeviceQueueFamilyProperties(physicalDevs[i], &queueFamilyPropCount, nullptr);
                VkQueueFamilyProperties queueFamilyProps[queueFamilyPropCount];
                vkGetPhysicalDeviceQueueFamilyProperties(physicalDevs[i], &queueFamilyPropCount, queueFamilyProps);
                // 表面查询支持
                for (int j = 0; j < queueFamilyPropCount; j++)
                {
                        VkBool32 surfaceSupport;
                        vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevs[i], i, surface, &surfaceSupport);
                        Print("Surface Support: %d", surfaceSupport);

                        if (surfaceSupport && queueFamilyProps[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)
                        {
                                selectedPhysicalDev = physicalDevs[i];
                                workQueueIndex = j;
                                presentQueueIndex = j;
                                break;
                        }
                }
        }
        if (nullptr == selectedPhysicalDev)
        {
                throw std::runtime_error("Can not find any suitable Physical device.");
        }

        VkPhysicalDeviceProperties devProps;
        vkGetPhysicalDeviceProperties(selectedPhysicalDev, &devProps);
        out_deviceInfo->physicalDev = selectedPhysicalDev;
        out_deviceInfo->physicalDevLimits = devProps.limits;
        out_queueInfo->workQueueIndex = workQueueIndex;
        out_queueInfo->presentQueueIndex = presentQueueIndex;
        vkGetPhysicalDeviceMemoryProperties(selectedPhysicalDev, &out_deviceInfo->physicalDevMemoProps);

        uint32_t availableDeviceExtensionCount;
        CALL_VK(vkEnumerateDeviceExtensionProperties(out_deviceInfo->physicalDev, nullptr, &availableDeviceExtensionCount, nullptr));
        VkExtensionProperties availableDeviceExtensions[availableDeviceExtensionCount];
        CALL_VK(vkEnumerateDeviceExtensionProperties(out_deviceInfo->physicalDev, nullptr, &availableDeviceExtensionCount, availableDeviceExtensions));
        const char *enableDeviceExtensions[32];
        uint32_t enableDeviceExtensionCount;
        checkFeatures("Device Extensions", true, true, validationDeviceExtensions, ARRAY_SIZE(validationDeviceExtensions),
                      availableDeviceExtensions, availableDeviceExtensionCount, enableDeviceExtensions, &enableDeviceExtensionCount);

        float queuePriority = 1.0f;
        VkDeviceQueueCreateInfo queueCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .queueFamilyIndex = out_queueInfo->workQueueIndex,
            .queueCount = 1,
            .pQueuePriorities = &queuePriority, // 队列优先级
        };

        VkDeviceCreateInfo deviceCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .queueCreateInfoCount = 1,
            .pQueueCreateInfos = &queueCreateInfo,
            .enabledLayerCount = 0,
            .ppEnabledLayerNames = nullptr,
            .enabledExtensionCount = enableDeviceExtensionCount,
            .ppEnabledExtensionNames = enableDeviceExtensionCount == 0 ? nullptr : enableDeviceExtensions};
        CALL_VK(vkCreateDevice(out_deviceInfo->physicalDev, &deviceCreateInfo, VK_ALLOC, &out_deviceInfo->device));

        vkGetDeviceQueue(out_deviceInfo->device, out_queueInfo->workQueueIndex, 0, &out_queueInfo->queue);
}

void VkHelper::printPhysicalDevLog(const VkPhysicalDeviceProperties &devProps)
{
        /****************** For log *********************/
        const uint32_t driverMajor = VK_VERSION_MAJOR(devProps.driverVersion);
        const uint32_t driverMinor = VK_VERSION_MINOR(devProps.driverVersion);
        const uint32_t driverPatch = VK_VERSION_PATCH(devProps.driverVersion);

        const uint32_t apiMajor = VK_VERSION_MAJOR(devProps.apiVersion);
        const uint32_t apiMinor = VK_VERSION_MINOR(devProps.apiVersion);
        const uint32_t apiPatch = VK_VERSION_PATCH(devProps.apiVersion);

        Print("---------------------------------");
        Print("Device Name          : %s", devProps.deviceName);
        Print("Device Type          : %s",
              ((devProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) ? "integrated GPU" : ((devProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ? "discrete GPU" : ((devProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU) ? "virtual GPU" : ((devProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) ? "CPU" : "unknown")))));
        Print("Vendor ID            : 0x%04X", devProps.vendorID);
        Print("Device ID            : 0x%04X", devProps.deviceID);
        Print("Driver Version       : %d.%d.%d", driverMajor, driverMinor, driverPatch);
        Print("API Version          : %d.%d.%d", apiMajor, apiMinor, apiPatch);
        /****************** For log *********************/
}

void VkHelper::createCommandPool(VkDevice device, uint32_t workQueueIndex, VkCommandPool *out_cmdPool)
{
        VkCommandPoolCreateInfo createInfo = {
            .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
            .pNext = nullptr,
            .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
            .queueFamilyIndex = workQueueIndex};
        CALL_VK(vkCreateCommandPool(device, &createInfo, VK_ALLOC, out_cmdPool));
}

void querySwapchainParam(VkPhysicalDevice physicalDev, VkSurfaceKHR surface, SwapchainParam *out_swapchainParam)
{
        // capabilities
        CALL_VK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDev, surface, &out_swapchainParam->capabilities));
        out_swapchainParam->extent = out_swapchainParam->capabilities.currentExtent;
        Print("Extent: %d, %d", out_swapchainParam->extent.width, out_swapchainParam->extent.height);

        // format
        uint32_t formatCount;
        vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDev, surface, &formatCount, nullptr);
        VkSurfaceFormatKHR formats[formatCount];
        vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDev, surface, &formatCount, formats);
        bool bFindFormat = false;
        if (formatCount == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
        {
                out_swapchainParam->format = {VK_FORMAT_B8G8R8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR};
                bFindFormat = true;
        }
        else
        {
                for (const auto &item : formats)
                {
                        if (item.format == VK_FORMAT_B8G8R8A8_UNORM && item.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR)
                        {
                                out_swapchainParam->format = item;
                                bFindFormat = true;
                                break;
                        }
                }
        }
        if (!bFindFormat)
        {
                out_swapchainParam->format = formats[0];
        }

        // present mode
        uint32_t presentModeCount;
        vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDev, surface, &presentModeCount, nullptr);
        VkPresentModeKHR presentModes[presentModeCount];
        vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDev, surface, &presentModeCount, presentModes);

        // VK_PRESENT_MODE_IMMEDIATE_KHR 立即提交，可能导致画面撕裂
        // VK_PRESENT_MODE_FIFO_KHR 先进先出的队列，队列满时需要等待，类似垂直同步
        // VK_PRESENT_MODE_FIFO_RELAXED_KHR 和上一模式的区别是：如果应用程序延迟，导致交换链的队列在上一次垂直回扫时为空， 那么，如果应用程序在下一次垂直回扫前提交图像，图像会立即被显示。这一模式可能会导致撕裂现象。
        // VK_PRESENT_MODE_MAILBOX_KHR 这一模式是第二种模式的另一个变种。它不会在交换链的队列满时阻塞应用程序，队列中的图像会被直接替换为应用程序新提交的图像。这一模式可以用来实现三倍缓冲，避免撕裂现象的同时减小了延迟问题
        bool bFoundPrentMode = false;
        for (const auto &item : presentModes)
        {
                if (VK_PRESENT_MODE_MAILBOX_KHR == item)
                {
                        out_swapchainParam->presentMode = item;
                        bFoundPrentMode = true;
                }
                else if (VK_PRESENT_MODE_IMMEDIATE_KHR == item)
                { // 目前大部分硬件不不支持VK_PRESENT_MODE_MAILBOX_KHR
                        out_swapchainParam->presentMode = item;
                        bFoundPrentMode = true;
                }
        }
        if (!bFoundPrentMode)
        {
                out_swapchainParam->presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
        }
}

void VkHelper::createSwapchain(VkPhysicalDevice physicalDev, VkDevice device, VkSurfaceKHR surface,
                               uint16_t workQueueIndex, uint16_t presentQueueIndex,
                               SwapchainParam *out_swapchainParam, VkSwapchainKHR *out_swapchain)
{
        querySwapchainParam(physicalDev, surface, out_swapchainParam);

        uint32_t queueFamilysArr[2] = {static_cast<uint32_t>(workQueueIndex), static_cast<uint32_t>(presentQueueIndex)};

        VkSwapchainKHR oldSwapchain;
        if (nullptr != out_swapchain && VK_NULL_HANDLE != *out_swapchain)
        {
                oldSwapchain = *out_swapchain;
        }

        VkSwapchainCreateInfoKHR swapchainCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
            .pNext = nullptr,
            .flags = 0,
            // 上文创建的窗口表面
            .surface = surface,
            .minImageCount = out_swapchainParam->capabilities.minImageCount,
            .imageFormat = out_swapchainParam->format.format,
            .imageColorSpace = out_swapchainParam->format.colorSpace,
            .imageExtent = out_swapchainParam->extent,
            .imageArrayLayers = 1,
            // 用为颜色附件
            .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
            .imageSharingMode = workQueueIndex == presentQueueIndex ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT,
            .queueFamilyIndexCount = static_cast<uint32_t>(workQueueIndex == presentQueueIndex ? 1 : 2),
            .pQueueFamilyIndices = workQueueIndex == presentQueueIndex ? nullptr : queueFamilysArr,
            .preTransform = out_swapchainParam->capabilities.currentTransform,
            .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, // 忽略掉alpha通道
            .presentMode = out_swapchainParam->presentMode,
            .clipped = VK_TRUE,
            .oldSwapchain = oldSwapchain};

        CALL_VK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, VK_ALLOC, out_swapchain));
}

void VkHelper::createRenderPass(VkDevice device, SwapchainParam swapchainParam, VkRenderPass *out_renderPass)
{
        VkAttachmentDescription attachDesc[] = {
            {.flags = 0,
             .format = swapchainParam.format.format,
             .samples = VK_SAMPLE_COUNT_1_BIT,
             // VK_ATTACHMENT_LOAD_OP_LOAD：保持附着的现有内容
             // VK_ATTACHMENT_LOAD_OP_CLEAR：使用一个常量值来清除附着的内容
             // VK_ATTACHMENT_LOAD_OP_DONT_CARE：不关心附着现存的内容
             .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
             .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
             // K_ATTACHMENT_STORE_OP_STORE：渲染的内容会被存储起来，以便之后读取
             // VK_ATTACHMENT_STORE_OP_DONT_CARE：渲染后，不会读取帧缓冲的内容
             .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
             .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
             .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
             .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR}};
        VkAttachmentReference colorAttachRef = {
            .attachment = 0,                                    // 0表示上文attachDesc[]数组的第1位
            .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // 作为颜色附件使用
        };

        VkSubpassDescription subpass = {
            .flags = 0,
            .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
            .inputAttachmentCount = 0,
            .pInputAttachments = nullptr,
            .colorAttachmentCount = 1,
            .pColorAttachments = &colorAttachRef,
            .preserveAttachmentCount = 0,
            .pPreserveAttachments = nullptr,
        };

        VkSubpassDependency dependency = {
            .srcSubpass = VK_SUBPASS_EXTERNAL, // 外部流程作为输入
            .dstSubpass = 0,
            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .srcAccessMask = 0,
            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // 颜色附件作为输出
            .dependencyFlags = 0,
        };

        VkRenderPassCreateInfo createInfo = {
            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .attachmentCount = ARRAY_SIZE(attachDesc),
            .pAttachments = attachDesc,
            .subpassCount = 1,
            .pSubpasses = &subpass,
            .dependencyCount = 1,
            .pDependencies = &dependency};

        CALL_VK(vkCreateRenderPass(device, &createInfo, VK_ALLOC, out_renderPass));
}

void VkHelper::createFramebuffer(VkDevice device, VkRenderPass renderPass,
                                 uint32_t width, uint32_t height,
                                 uint32_t framebufferCount, VkImageView *imageViews, VkFramebuffer *out_framebuffers)
{
        for (uint32_t i = 0; i < framebufferCount; i++)
        {
                VkImageView attachment[] = {
                    imageViews[i],
                };
                VkFramebufferCreateInfo createInfo = {
                    .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                    .pNext = nullptr,
                    .flags = 0,
                    .renderPass = renderPass,
                    .attachmentCount = ARRAY_SIZE(attachment),
                    .pAttachments = attachment,
                    .width = width,
                    .height = height,
                    .layers = 1};
                CALL_VK(vkCreateFramebuffer(device, &createInfo, VK_ALLOC, &out_framebuffers[i]));
        }
}

void VkHelper::createPipelineLayout(VkDevice device, VkPipelineLayout *out_pipelineLayout)
{
        VkPipelineLayoutCreateInfo createInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .setLayoutCount = 0,
            .pSetLayouts = nullptr,
            .pushConstantRangeCount = 0,
            .pPushConstantRanges = nullptr};
        CALL_VK(vkCreatePipelineLayout(device, &createInfo, VK_ALLOC, out_pipelineLayout));
}

VkShaderModule VkHelper::createShaderModule(VkDevice device, std::vector<char> &code)
{
        VkShaderModuleCreateInfo createInfo;
        createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
        createInfo.pNext = nullptr;
        createInfo.flags = 0;
        createInfo.codeSize = code.size();
        createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data());

        VkShaderModule shaderModule;
        if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
        {
                throw std::runtime_error("failed to create shader module!");
        }
        return shaderModule;
}

void VkHelper::createPipeline(VkDevice device, VkPipelineLayout pipelineLayout, VkRenderPass renderPass,
                              VkShaderModule vertShaderModule, VkShaderModule fragShaderModule, SwapchainParam swapchainParam,
                              VkPipeline *out_pipeline)
{
        VkPipelineShaderStageCreateInfo stages[] = {
            {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
             .pNext = nullptr,
             .flags = 0,
             .stage = VK_SHADER_STAGE_VERTEX_BIT,
             .module = vertShaderModule,
             .pName = "main",
             .pSpecializationInfo = nullptr},
            {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
             .pNext = nullptr,
             .flags = 0,
             .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
             .module = fragShaderModule,
             .pName = "main",
             .pSpecializationInfo = nullptr}};

        VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .vertexBindingDescriptionCount = 0,
            .pVertexBindingDescriptions = nullptr,
            .vertexAttributeDescriptionCount = 0,
            .pVertexAttributeDescriptions = nullptr};

        VkPipelineInputAssemblyStateCreateInfo assemblyStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
            .primitiveRestartEnable = VK_FALSE,
        };

        VkViewport viewport = {
            .x = 0,
            .y = 0,
            .width = static_cast<float>(swapchainParam.extent.width),
            .height = static_cast<float>(swapchainParam.extent.height),
            .minDepth = 0,
            .maxDepth = 1};

        VkRect2D scissor = {
            .offset = {0, 0},
            .extent = swapchainParam.extent,
        };

        VkPipelineViewportStateCreateInfo viewportStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .viewportCount = 1,
            .pViewports = &viewport,
            .scissorCount = 1,
            .pScissors = &scissor};

        VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .depthClampEnable = VK_FALSE,        // 计算阴影
            .rasterizerDiscardEnable = VK_FALSE, // VK_TRUE表示所有几何图元都不能通过光栅化阶段。这一设置会禁止一切片段输出到帧缓冲
            // VK_POLYGON_MODE_FILL：整个多边形，包括多边形内部都产生片段
            // VK_POLYGON_MODE_LINE：只有多边形的边会产生片段
            // VK_POLYGON_MODE_POINT：只有多边形的顶点会产生片段
            .polygonMode = VK_POLYGON_MODE_FILL,
            .cullMode = VK_CULL_MODE_BACK_BIT,            // 表面剔除
            .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, // 逆时针标识为前面
            .depthBiasEnable = VK_FALSE,
            .depthBiasConstantFactor = 0,
            .depthBiasClamp = 0,
            .depthBiasSlopeFactor = 0,
            .lineWidth = 1.0f,
        };

        VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
            .sampleShadingEnable = VK_FALSE, // 抗锯齿
            .minSampleShading = 1.0f,
            .pSampleMask = nullptr,
            .alphaToCoverageEnable = VK_FALSE,
            .alphaToOneEnable = VK_FALSE};

        VkPipelineColorBlendAttachmentState attachmentState = {
            //    VkColorComponentFlags    colorWriteMask;
            .blendEnable = VK_FALSE,
            .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
            .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
            .colorBlendOp = VK_BLEND_OP_ADD,
            .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
            .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
            .alphaBlendOp = VK_BLEND_OP_ADD,
            .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};

        VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .logicOpEnable = VK_FALSE,
            .logicOp = VK_LOGIC_OP_CLEAR,
            .attachmentCount = 1,
            .pAttachments = &attachmentState,
            .blendConstants = {0, 0, 0, 0},
        };

        VkGraphicsPipelineCreateInfo createInfo = {
            .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .stageCount = ARRAY_SIZE(stages),
            .pStages = stages,
            .pVertexInputState = &vertexInputStateCreateInfo,
            .pInputAssemblyState = &assemblyStateCreateInfo,
            .pTessellationState = nullptr,
            .pViewportState = &viewportStateCreateInfo,
            .pRasterizationState = &rasterizationStateCreateInfo,
            .pMultisampleState = &multisampleStateCreateInfo,
            .pDepthStencilState = nullptr,
            .pColorBlendState = &colorBlendStateCreateInfo,
            .pDynamicState = nullptr,
            .layout = pipelineLayout,
            .renderPass = renderPass,
            .subpass = 0,
            .basePipelineHandle = nullptr,
            .basePipelineIndex = 0};

        CALL_VK(vkCreateGraphicsPipelines(device, nullptr, 1, &createInfo, VK_ALLOC, out_pipeline));
}

void VkHelper::allocateCommandBuffers(VkDevice device, VkCommandPool cmdPool, uint32_t cmdBufferCount, VkCommandBuffer *cmdBuffers)
{
        VkCommandBufferAllocateInfo allocateInfo = {
            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
            .pNext = nullptr,
            .commandPool = cmdPool,
            .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
            .commandBufferCount = cmdBufferCount};

        CALL_VK(vkAllocateCommandBuffers(device, &allocateInfo, cmdBuffers));
}